/**
 *  Filename: FDSet.java
 *  Purpose:  Holds a set of functional dependencies and calculates the X-Closure for
 *            a set of attributes based on the functional dependencies.
 *  Date:     Nov 1, 2009
 *  Author:   Philippe Gambling
 */
package com.pgamb.Hwk4;

import java.util.LinkedHashMap;
import java.util.TreeSet;
import java.util.Map;
import java.util.Set;

import com.pgamb.Hwk4.Helpers;

/**
 * @author Philippe Gambling
 *
 */
public class FDSet {

	public int length;
	private Map<Set<Character>,Set<Character>> funcDepMap;
	
	
	/**
	 *  Name: FDSet
	 *  Description: Default constructor
	 *  Date: 11/02/2009
	 *  @author Philippe Gambling
	 */
	public FDSet() {
		this.length = 0;
		this.funcDepMap = new LinkedHashMap<Set<Character>,Set<Character>>();
	}


	/**
	 *  Name: AddFuncDep
	 *  Description: Adds a single functional dependency to the set of FDs
	 *  Date: 11/02/2009
	 *  @author Philippe Gambling
	 *  @param lhs left hand side attributes of the functional dependency
	 *  @param rhs right hand side attributes of the functional dependency
	 */
	public void AddFuncDep(String lhs, String rhs) {
		Set<Character> key = new TreeSet<Character>();
		Set<Character> value = new TreeSet<Character>();
		
		// Convert lhs and rhs Strings to Sets of Characters
		// to eliminate duplicate attributes
		Helpers.String2Set(lhs,key);
		Helpers.String2Set(rhs,value);
		
		// Add any existing right hand side attributes to value
		// before adding the key and value to funcDepMap
		// in case this key is already in the map
		if(this.funcDepMap.containsKey(key)) {
			value.addAll(this.funcDepMap.get(key));
		}
		this.funcDepMap.put(key, value);
	}
	
	/**
	 *  Name: XClosure
	 *  Description: Determines the X-closure for a set of attributes with respect
	 *               to the functional dependencies in FDSet
	 *  Date: 11/02/2009
	 *  @author Philippe Gambling
	 *  @param inputs function determines the x-closure for this set of attributes.
	 *  @return The resulting closure.
	 */
	public Set<Character> XClosure(Set<Character> inputs) {
		Set <Character> closure = new TreeSet<Character>(inputs);
		int prevSize = 0;
		
		while(prevSize < closure.size()) {
			prevSize = closure.size();
			
			for( Set<Character> lhs : this.funcDepMap.keySet()) {
				if(closure.containsAll(lhs)) 
					closure.addAll(this.funcDepMap.get(lhs));
			}
		}
		
		return closure;
	}

	@Override
	/**
	 *  Name: toString
	 *  Description: Returns a String representation of FDSet. 
	 *               Used for testing purposes.
	 *  Date: 11/02/2009
	 *  @author Philippe Gambling
     *  @return String representation of functional dependencies contained in FDSet
	 */
	public String toString() {
		String retStr = new String("{ ");
		boolean firstEntry = true;
		for(Map.Entry<Set<Character>,Set<Character>> entry : this.funcDepMap.entrySet()) {
			if( firstEntry == false ) 
				retStr = retStr + ", ";
			else 
				firstEntry = false;
			
			retStr = retStr + entry.getKey().toString() + " --> " + entry.getValue().toString();
		}
		retStr = retStr + " }";
		return retStr;
	}
	
	public int size() {
		return this.funcDepMap.size();
	}
}
